"use strict";

define(['config', 'url', 'base64', 'ajax'], (config, url, base64, ajax) => {
    return () => {
        var base = config.api;
        if (base == null) return;
        if (base.endsWith("/")) base = base.substr(0, base.length-1);


        //----------------------------------------------------------------------
        // login and authentication

        var login = () => {
            var loc = url.parse(location.href);
            delete loc.args.name;
            delete loc.args.username;
            var callback = url.build(loc.base, loc.args);
            location.href = url.build(base + "/login", { redirect: callback });
        };

        var roles = [
            '信息部管理员',
            '销售部业务员', '销售部经理',
            '售后部业务员', '售后部经理', '售后部信息员'
        ];

        var parse_token = token => {
            // parse
            var shards = token.split('.');
            if (shards.length != 2) return;
            var raw_token = JSON.parse(base64.decode(shards[0]));

            // simple validation
            if (roles.indexOf(raw_token.role) === -1) return;
            if (raw_token.seq == null) return;

            return raw_token;
        };

        var init = () => {
            var info = url.parse(location.href);
            var args = info.args;
            var token = info.anchor;
            if (token == null) return;
            if (args.name == null) return;
            if (args.username == null) return;

            token = token.substr(1);
            var raw_token = parse_token(token);
            if (raw_token == null) return;

            return {
                base,
                login,
                roles,
                token: token,
                name: args.name,
                username: args.username,
                role: raw_token.role,
            };
        };

        // check login
        var api = init();
        if (api == null) return login();


        //----------------------------------------------------------------------
        // APIs

        var zip_args = (arg_names, args) => {
            var result = {};
            arg_names.forEach((name, i) => {
                var arg = args[i];
                if (arg == null)
                    throw "bad api call: null not allowed in ("
                        + name + ") of (" + arg_names + ")";
                result[name] = arg;
            });
            return result;
        };

        var make_scalar_api_invoker = (post, addr, arg_names) => {
            return (...args) => {
                var remain = args.length - arg_names.length;
                if (remain !== 1 && remain !== 2)
                    throw "bad api call: expect " + (arg_names.length+1) + " or "
                        + (arg_names.length+2)
                        + " arguments (" + arg_names + ",callback,[update]), but "
                        + args.length + " were given.";

                var callback = args[arg_names.length];
                if (typeof callback !== 'function') throw "bad api call: callback must be a function";
                var update = args[arg_names.length+1];
                if (update == null) update = () => {};
                if (typeof update !== 'function') throw "bad api call: optional 'update' must be a function";

                var ok = () => callback(null);
                var fail = error => {
                    if (error === 'denied') return login();

                    console.error(error);
                    if (error === 'bad argument: not unique')
                        return callback("不唯一");
                    if (error.endsWith('outdated')) {
                        update();
                        return callback("数据已过期，没有进行任何操作");
                    }
                    if (error.startsWith('bad argument:'))
                        return callback("参数错误：" + error);
                    callback("服务器错误");
                };

                var zargs = zip_args(arg_names, args);
                zargs.token = api.token;
                ajax.json(post, addr, zargs, ok, fail);
            };
        };

        var make_list_api_invoker = (addr, arg_names) => {
            return (...args) => {
                var zargs = zip_args(arg_names, args);
                zargs.token = api.token;

                return () => {
                    var after = -1;
                    var end = false;

                    return callback => {
                        if (typeof callback !== 'function')
                            throw "callback must be a function";

                        var ok = list => {
                            if (list.length === 0) end = true;
                            else after = list[list.length-1].id;
                            callback(list);
                        };
                        var fail = error => {
                            if (error === 'denied') return login();
                            console.error(error);
                            callback(null);
                        };

                        if (end) setTimeout(() => callback([]));    // setTimeout to emulate promise
                        else {
                            zargs.after = after;
                            ajax.get_json(addr, zargs, ok, fail);
                        }
                    };
                };
            };
        };

        var make_api_invoker = spec => {
            var arg_names = spec.split(/\s+/);
            var addr = arg_names.shift();

            var invoker;
            if (addr.startsWith('@')) {
                addr = addr.substr(1);
                invoker = make_list_api_invoker(base + "/api" + addr, arg_names);
            }
            else {
                var post = false;
                if (addr.startsWith('!')) {
                    post = true;
                    addr = addr.substr(1);
                }
                invoker = make_scalar_api_invoker(post, base + "/api" + addr, arg_names);
            }

            var name = addr.substr(1).split('/').join('_');
            return { name, invoker };
        };

        var populate_api = specs =>
            specs.map(make_api_invoker)
                .forEach(x => api[x.name] = x.invoker);

        populate_api([
            '/user/revoke/self',
            '/user/leave user',
            '/user/validate',
            '/user/password user new',
            '/user/password/self new',
            '/user/role user new',
            '/user/name user new',
            '!/user/add username password role name',
            '@/user/list',

            '!/sale/add purpose budget',
            '/sale/cancel sale',
            '/sale/approve sale',
            '!/sale/reject sale reason',
            '@/sale/list',
            '@/sale/list/self',
            '!/sale/activity/add sale activity cost',
            '@/sale/activity/list sale',
            '@/sale/activity/list/self sale',

            '!/ticket/add complaint customer contact',
            '/ticket/assign ticket user',
            '@/ticket/list',
            '@/ticket/list/self',
            '@/ticket/list/latest',
            '@/ticket/list/free',
            '/ticket/done assignment',
            '!/ticket/fail assignment detail',
            '!/ticket/waive assignment detail',
        ]);

        return api;
    };
});

